package com.tc.vms.log;

import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;

/**
 * slf4j 是一个接口继承
 */
public class VmsClientLogger {
    public static final String VMS_CLIENT_LOGGER_NAME = "vms_client_logger";
    private static Logger log;

    static {
        log = createLogger(VMS_CLIENT_LOGGER_NAME);
    }

    private static Logger createLogger(final String loggerName) {
        String logConfigFilePath = System
                .getProperty("vms.client.log.configFile", System.getenv("VMS_CLIENT_LOG_CONFIGFILE"));
        Boolean isloadconfig = Boolean.parseBoolean(System.getProperty("vms.client.log.loadconfig", "true"));

        final String log4j_resource_file = System
                .getProperty("vms.client.log4j.resource.fileName", "log4j_vms_client.xml");

        final String logback_resource_file = System
                .getProperty("vms.client.logback.resource.fileName", "logback_vms_client.xml");
        final String log4j2_resource_file = System.getProperty("vms.client.log4j2.resource.fileName",
                "log4j2_vms_clients.xml");

        if (isloadconfig) {
            try {
                ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory();
                Class classType = iLoggerFactory.getClass();
                if (classType.getName().equals("org.slf4j.impl.Log4jLoggerFactory")) {
                    Class<?> DOMConfigurator = null;
                    Object DOMConfiguratorObj = null;
                    DOMConfigurator = Class.forName("org.apache.log4j.xml.DOMConfigurator");
                    DOMConfiguratorObj = DOMConfigurator.newInstance();
                    if (null == logConfigFilePath) {
                        Method configure = DOMConfiguratorObj.getClass().getMethod("configure", URL.class);
                        URL url = VmsClientLogger.class.getClassLoader().getResource(log4j_resource_file);
                        configure.invoke(DOMConfiguratorObj, url);
                    } else {
                        Method configure = DOMConfiguratorObj.getClass().getMethod("configure", String.class);
                        configure.invoke(DOMConfiguratorObj, logConfigFilePath);
                    }

                } else if (classType.getName().equals("ch.qos.logback.classic.LoggerContext")) {
                    Class<?> joranConfigurator = null;
                    Class<?> context = Class.forName("ch.qos.logback.core.Context");
                    Object joranConfiguratoroObj = null;
                    joranConfigurator = Class.forName("ch.qos.logback.classic.joran.JoranConfigurator");
                    joranConfiguratoroObj = joranConfigurator.newInstance();
                    Method setContext = joranConfiguratoroObj.getClass().getMethod("setContext", context);
                    setContext.invoke(joranConfiguratoroObj, iLoggerFactory);
                    if (null == logConfigFilePath) {
                        URL url = VmsClientLogger.class.getClassLoader().getResource(logback_resource_file);
                        Method doConfigure = joranConfiguratoroObj.getClass().getMethod("doConfigure", URL.class);
                        doConfigure.invoke(joranConfiguratoroObj, url);
                    } else {
                        Method doConfigure = joranConfiguratoroObj.getClass().getMethod("doConfigure", String.class);
                        doConfigure.invoke(joranConfiguratoroObj, logConfigFilePath);
                    }
                } else if (classType.getName().equals("org.apache.logging.slf4j.Log4jLoggerFactory")) {
                    // log4j2 configuration setting

                    Class<?> configurationSourceCls = Class
                            .forName("org.apache.logging.log4j.core.config.ConfigurationSource");
                    Class<?> xmlConfigurationCls = Class
                            .forName("org.apache.logging.log4j.core.config.xml.XmlConfiguration");
                    Class<?> configurationCls = Class.forName("org.apache.logging.log4j.core.config.Configuration");
                    Class<?> loggerContextCls = Class.forName("org.apache.logging.log4j.core.LoggerContext");
                    Class<?> log4jLoggerCls = Class.forName("org.apache.logging.slf4j.Log4jLogger");
                    Class<?> extendedLoggerCls = Class.forName("org.apache.logging.log4j.spi.ExtendedLogger");

                    Constructor<?> configSourceConstructor = configurationSourceCls
                            .getDeclaredConstructor(InputStream.class);
                    Constructor<?> xmlConfigurationConstructor = xmlConfigurationCls
                            .getDeclaredConstructor(configurationSourceCls);
                    Constructor<?> loggerContextConstructor = loggerContextCls.getDeclaredConstructor(String.class);
                    Constructor<?> log4jLoggerConstructor = log4jLoggerCls.getDeclaredConstructor(extendedLoggerCls,
                            String.class);

                    InputStream configFileInput = null;
                    if (null == logConfigFilePath) {
                        configFileInput = VmsClientLogger.class.getClassLoader()
                                .getResourceAsStream(log4j2_resource_file);
                    } else {
                        configFileInput = new FileInputStream(new File(logConfigFilePath));
                    }
                    Object configurationSourceInst = configSourceConstructor.newInstance(configFileInput);
                    Object xmlConfigurationInst = xmlConfigurationConstructor.newInstance(configurationSourceInst);
                    Object loggerContextInst = loggerContextConstructor.newInstance(VmsClientLogger.class.getName());
                    Method loggerContextStartMethod = loggerContextCls.getDeclaredMethod("start", configurationCls);
                    loggerContextStartMethod.invoke(loggerContextInst, xmlConfigurationInst);
                    Method loggerContextGetLoggerMethod = loggerContextCls.getDeclaredMethod("getLogger", String.class);
                    log = (Logger) log4jLoggerConstructor.newInstance(
                            loggerContextGetLoggerMethod.invoke(loggerContextInst, loggerName),
                            loggerName);
                    return log;
                }
            } catch (Exception e) {
                System.err.println(e.getCause());
            }
        }
        return LoggerFactory.getLogger(loggerName);
    }

    public static Logger getLog() {
        return log;
    }

    public static void setLoggerLevel(boolean isDebugLog) {
        Class<?> actualLogger = log.getClass();
        if (actualLogger.getName().equals("org.slf4j.impl.Log4jLoggerAdapter")) {
            try {
                Field logField = actualLogger.getDeclaredField("logger");
                Class<?> lvlClass = Class.forName("org.apache.log4j.Level");
                Class<?> logClass = Class.forName("org.apache.log4j.Category");
                Method logMethod = logClass.getDeclaredMethod("setLevel", lvlClass);

                logField.setAccessible(true);
                Object tmpLog = logField.get(log);

                if (isDebugLog) {
                    Field lField = lvlClass.getField("ALL");
                    logMethod.invoke(tmpLog, lField.get(null));
                } else {
                    Field lField = lvlClass.getField("INFO");
                    logMethod.invoke(tmpLog, lField.get(null));
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (actualLogger.getName().equals("ch.qos.logback.classic.Logger")) {
            try {
                Class<?> lClass = Class.forName("ch.qos.logback.classic.Level");
                Method logMethod = actualLogger.getDeclaredMethod("setLevel", lClass);
                if (isDebugLog) {
                    Field lField = lClass.getField("ALL");
                    logMethod.invoke(log, lField.get(null));
                } else {
                    Field lField = lClass.getField("INFO");
                    logMethod.invoke(log, lField.get(null));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void setLog(Logger log) {
        VmsClientLogger.log = log;
    }
}
